《JavaScript启示录》第一章 JavaScript对象

引用本书的前言——“本书的撰写意图是通过考察原生JavaScript对象和不同环境对原生对象的支持的细微差别,来给读者展现准确的JavaScript世界观:复杂值、原始值、作用域、继承、head对象等。我希望本书是关于ECMAScript第三版规范的简单易懂的总结,重点介绍JavaScript中对象的特性。”

###创建对象
在JavaScript中,对象为“王”:JavaScript里的几乎所有东西都是对象或者用起来像对象。理解了对象,就能够理解JavaScript。

对象只是一组有命名值(也称为属性)集合的容器,以作者cody为例,我们可以用简单的语言在表格中表达cody:

上表中的cody一词只是一组属性名和对应值的标签,这些属性和值构成了cody。正如表中所示:cody现在活着,33岁,男性。

然而JavaScript不会用表格来表达,它是用对象来表达的,就先cody表格中包含的那样,将上述表格转化成实际的JavaScript对象应该是这样的:

1
2
3
4
5
6
7
8
9
<!DOCTYPE html><html lang="en"><body><script>
// 创建cody对象
var cody = new Object();
// 为cody对象的各种属性赋值(使用点表示法)
cody.living = true;
cody.age = 33;
cody.gender = 'male';
console.log(cody); // 输出 Object {living: true, age: 33, gender: "male"}
</script></body></html>

在JSFiddle中查看
最重要的是要记住:对象只是属性的容器,每个属性都有一个名称和一个值。JavaScript采用具有命名值属性的容器(即对象)这一概念作为在JavaScript中表达值的构建块。cody对象是一个值,通过创建对象将这个值表示为JavaScript对象,给对象命名,然后将属性赋值给对象。

到目前为止,我们所讨论的cody对象只有静态信息,这样cody对象和JSON没有区别。为了使cody对象有生命力,需要添加一个属性:方法(method)。方法用于执行函数(function)。确切的说,在JavaScript中,方法是包含Function()对象的属性,其目的是对函数内部的对象进行操作。

更新cody表格,加入getGender方法

用JavaScript代码表示,更新后的上述cody表格中的getGender方法应该是这样的:

1
2
3
4
5
6
7
8
<!DOCTYPE html><html lang="en"><body><script>
var cody = new Object();
cody.living = true;
cody.age = 33;
cody.gender = 'male';
cody.getGender = function(){return cody.gender;};
console.log(cody.getGender()); // 输出 "male"
</script></body></html>

在JSFiddle中查看
getGender方法是cody对象的属性,用于返回cody的其他属性值:存储在gender属性中的”male”值。必须知道的是,如果没有方法,除了用于存储静态属性外,对象就没有太大用处。

截至目前,我们讨论的cody对象是一种Object()对象。通过调用Object()构造函数而得到的空对象创建了cody对象。对于cody对象示例,使用Object()构造函数来生成空对象,然后将它命名为cody。鉴于cody是用Object()构造函数构造出的一个对象,所以将cody对象称为Object()对象。真正需要了解的是,除了创建像cody这样简单的Object()对象外,JavaScript中的大多数值都是对象(“foo”、5和true等原始值例外,但他们拥有等效包装器对象)。

Object()构造函数创建的cody对象与通过String()构造函数创建的字符串对象没有太大区别,通过下面例子可以验证:

1
2
3
4
5
6
7
8
9
<!DOCTYPE html><html lang="en"><body><script>
var myObject = new Object(); // 创建一个Object()对象
myObject['0'] = 'f';
myObject['1'] = 'o';
myObject['2'] = 'o';
console.log(myObject); // 输出 Object { 0="f", 1="o", 2="o"}
var myString = new String('foo'); // produces a String() object
console.log(myString); // 输出 foo { 0="f", 1="o", 2="o"}
</script></body></html>

在JSFiddle中查看
正如这里所显示的,myObject和myString都是对象!他们都可以有属性、继承属性,并且都由构造函数生成。包含”foo”字符串值的myString变量看似很简单,但在其表面之下有一个对象结构,这两个对象虽然在本质上是相同的,但在类型上是不同的。更重要的是,JavaScript是使用对象来表示值的。

JavaScript将String()Object()构造函数作为其自身的语言,以便使String()对象和Object()对象的创建变得简单。下面例子定义了一个非原生的自定义Person()构造函数,以便用他们创建people对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html><html lang="en"><body><script>
// 定义Person构造函数,以便稍后创建自定义的Person()对象
var Person = function(living, age, gender) {
this.living = living;
this.age = age;
this.gender = gender;
this.getGender = function() {return this.gender;};
};
// 实例化Person对象,并将他保存到cody变量
var cody = new Person(true, 33, 'male');
console.log(cody);
/* 下面的String()构造函数由JavaScript自身所定义,有相同的模式,因为string构造函数是JavaScript内置的,获取字符串所要做的就是将它实例化,但无论是用原生的String()还是自定义的构造函数Person(),模式都是一样的。 */
// 实例化一个String对象并保存到myString变量
var myString = new String('foo');
console.log(myString);
</script></body></html>

在JSFiddle中查看
自定义的Person()构造函数可以生成person对象,就像原生String()构造函数可以生成字符串对象一样。Person()构造函数的能力和延展性并不比原生String()构造函数或JavaScript中其他原生构造函数差。

需要注意,在示例中,Object()构造函数和Person()构造函数可以产生相同的结果。两者都可以生成具有相同属性和属性方法的相同对象,下例来证明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html><html lang="en"><body><script>
// 使用Object()构造函数创建codyA对象
var codyA = new Object();
codyA.living = true;
codyA.age = 33;
codyA.gender = 'male';
codyA.getGender = function() {return codyA.gender;};
console.log(codyA); // 输出 Object {living=true, age=33, gender="male", ...}
/* 下面创建了同样的cody对象,但不是使用原生Object()构造函数创建一次性的cody对象,首先定义Person()构造函数来创建cody对象(或者其他任意Person对象),然后使用"new"关键字实例化。 */
var Person = function(living, age, gender) {
this.living = living;
this.age = age;
this.gender = gender;
this.getGender = function() {return this.gender;};
};
// 输出 Object {living=true, age=33, gender="male", ...}
var codyB = new Person(true, 33, 'male');
console.log(codyB);
</script></body></html>

在JSFiddle中查看
codyA和codyB对象之间的主要区别不在于对象本身,而是在于生成对象的构造函数。codyA对象是用Object()构造函数实例化生成的。Person()构造函数创建了codyB,但也可以把Person()构造函数当成一个强大的、集中定义对象的“工厂”,用于创建更多的Person()对象。为自定义对象创建自定义构造函数的同时,也为Person()实例创建了原型继承。

这两个方案都能够创建相同的复杂对象,这两个模式最常用于构造对象。

JavaScript实际上是一种预包装若干原生对象构造函数的语言。这些构造函数用于生成一些表达特定类型值(如数字、字符串、函数、对象、数组等)的复杂对象,同样也可以通过Function()对象创建自定义的对象构造函数(例如本例中的Person())。不管是否是用于创建对象的模式,产生的最终结果都是创建一个复杂的对象。

Fork me on GitHub